import { WsClient } from "tsrpc-browser";
import { GameMgr, GameMode } from "../../module_basic/scripts/GameMgr";
import { SubGameMgr } from "../../module_basic/scripts/SubGameMgr";
import { UserMgr } from "../../module_basic/scripts/UserMgr";
import { ServiceType } from "../../module_basic/shared/protocols/serviceProto_worldServer";
import { IGomokuGameData, IGomokuPlayer } from "../../module_basic/shared/protocols/worldServer/gomoku/GomokuTypeDef";
import { MsgGameDataChangedPush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgGameDataChangedPush";
import { director } from "cc";
import { MsgPlayerComesPush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgPlayerComesPush";
import { MsgPlayerDataChangedPush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgPlayerDataChangedPush";
import { MsgPlayerLeavesPush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgPlayerLeavesPush";
import { MsgGameBeginPush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgGameBeginPush";
import { GomokuBoardMgr } from "../../module_basic/shared/GomokuBoardMgr";
import { MsgGameOverPush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgGameOverPush";
import { MsgPlacePiecePush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgPlacePiecePush";
import { WorldMgr } from "../../module_basic/scripts/WorldMgr";
import { MsgGameDataSyncPush } from "../../module_basic/shared/protocols/worldServer/gomoku/MsgGameDataSyncPush";

export class GomokuGameEvent {
    public static TURN_PLAYER_CHANGED = 'GomokuGameEvent.TURN_PLAYER_CHANGED';
    public static PLAYER_COMES = 'GomokuGameEvent.PLAYER_COMES';
    public static PLAYER_LEAVES = 'GomokuGameEvent.PLAYER_LEAVES';
    public static GAME_BEGIN = 'GomokuGameEvent.GAME_BEGIN';
    public static GAME_OVER = 'GomokuGameEvent.GAME_OVER';
    public static PLACE_PIECE = 'GomokuGameEvent.PLACE_PIECE';
}

export class GomokuGameMgr extends GameMgr {
    private static _inst: GomokuGameMgr;
    public static get inst(): GomokuGameMgr {
        if (!this._inst) {
            this._inst = new GomokuGameMgr();
        }
        return this._inst;
    }

    private _gameData: IGomokuGameData;
    private _boardMgr: GomokuBoardMgr = new GomokuBoardMgr();

    public get gameData(): Readonly<IGomokuGameData> {
        return this._gameData;
    }

    public get boardMgr(): GomokuBoardMgr {
        return this._boardMgr;
    }

    initNetMsgHandlers(conn: WsClient<ServiceType>) {
        super.initNetMsgHandlers(conn);

        conn.listenMsg("gomoku/GameDataSyncPush", this.onNet_game_data_sync_push.bind(this));
        conn.listenMsg("gomoku/GameDataChangedPush", this.onNet_game_data_changed_push.bind(this));

        conn.listenMsg("gomoku/PlayerComesPush", this.onNet_player_comes_push.bind(this));
        conn.listenMsg("gomoku/PlayerDataChangedPush", this.onNet_player_data_changed_push.bind(this));
        conn.listenMsg("gomoku/PlayerLeavesPush", this.onNet_player_leaves_push.bind(this));

        conn.listenMsg("gomoku/GameBeginPush", this.onNet_game_begin_push.bind(this));
        conn.listenMsg("gomoku/GameOverPush", this.onNet_game_over_push.bind(this));

        conn.listenMsg("gomoku/PlacePiecePush", this.onNet_place_piece_push.bind(this));
    }

    public reset() {
        super.reset();
        this._gameMode = GameMode.SOLO;
    }

    public initAIMode() {
        this.gameMode = GameMode.AI;
        this._data = {
            id: 'ai-vs',
            displayId: 'ai-vs',
            name: 'ai-bs',
            maxUser: 2,
            userList: [{ uid: UserMgr.inst.uid, ready: true, isPlayer: true }, { uid: 'ai', ready: true, isPlayer: true }],
            maxPlayerNum: 2,
            playerNum: 2,
            isPlaying: true,
            messages: []
        }
        this._gameData = {
            players: [{ uid: UserMgr.inst.userId, color: 'black' }, { uid: 'ai', color: 'white' }],
            currentPlayer: UserMgr.inst.userId,
        }
        this._boardMgr.reset();
    }

    public changeTurnPlayer() {
        if (this.gameMode != GameMode.AI) {
            return;
        }

        if (this._gameData.currentPlayer == this._gameData.players[0].uid) {
            this._gameData.currentPlayer = this.gameData.players[1].uid;
        }
        else {
            this._gameData.currentPlayer = this.gameData.players[0].uid;
        }
        director.getScene().emit(GomokuGameEvent.TURN_PLAYER_CHANGED, this._gameData.currentPlayer);
    }

    public get selfPlayer(): IGomokuPlayer {
        return this.getPlayer(UserMgr.inst.userId);
    }

    private get selfPlayerIndex(): number {
        return this.getPlayerIndex(UserMgr.inst.userId);
    }

    public get leftUserId(): string {
        if (!this._gameData || this._gameData.players.length == 0) {
            return '';
        }

        if (this.isPlayer) {
            return UserMgr.inst.userId;
        }
        else {
            return this._gameData.players[0].uid;
        }
    }

    public get rightUserId(): string {
        if (!this._gameData || this._gameData.players.length == 0) {
            return '';
        }

        if (this.isPlayer) {
            if (this._gameData.players[0].uid != UserMgr.inst.userId) {
                return this._gameData.players[0].uid;
            }
            else {
                let p = this._gameData.players[1];
                return p ? p.uid : '';
            }
        }
        else {
            let p = this._gameData.players[1];
            return p ? p.uid : '';
        }
    }

    public get isMyTurn(): boolean {
        if (!this._gameData) {
            return false;
        }

        if (!this.isPlayer) {
            return false;
        }
        if (!this._gameData.currentPlayer) {
            return false;
        }
        return this._gameData.currentPlayer == UserMgr.inst.userId;
    }

    public getPlayer(userId: string): IGomokuPlayer {
        if (!this._gameData || !this._gameData.players) {
            return null;
        }
        for (let i = 0; i < this._gameData.players.length; ++i) {
            let p = this._gameData.players[i];
            if (p.uid == userId) {
                return p;
            }
        }
        return null;
    }

    private getPlayerIndex(userId: string): number {
        for (let i = 0; i < this._gameData.players.length; ++i) {
            if (this._gameData.players[i].uid == userId) {
                return i;
            }
        }
        return -1;
    }

    public async rpc_PlacePiece(gridX: number, gridY: number) {
        if (this._gameMode == GameMode.AI) {
            let v = 1;
            this._boardMgr.setGrid(gridX, gridY, v);
            let msg = {
                uid: GomokuGameMgr.inst.gameData.currentPlayer,
                gridX: gridX,
                gridY: gridY,
                value: v
            };
            director.getScene().emit(GomokuGameEvent.PLACE_PIECE, msg);

            this.changeTurnPlayer();
            return { isSucc: true, err: null };
        }
        else {
            return await WorldMgr.worldConn.callApi("gomoku/PlacePiece", { gridX: gridX, gridY: gridY });
        }
    }

    // ============= MESSAGE HANDLER ============
    /**
     * This message will arrive before login result.
     * 这个消息会在登录成功返回之前收到。
    */
    onNet_game_data_sync_push(msg: MsgGameDataSyncPush) {
        this._gameData = msg.data;
        this._boardMgr.gridData = this._gameData.boardData;
    }

    onNet_game_data_changed_push(msg: MsgGameDataChangedPush) {
        if (!this._gameData) {
            return;
        }

        if (msg.currentPlayer !== undefined) {
            this._gameData.currentPlayer = msg.currentPlayer;
            director.getScene().emit(GomokuGameEvent.TURN_PLAYER_CHANGED, msg.currentPlayer);
        }

        if (msg.boardData !== undefined) {
            this._gameData.boardData = msg.boardData;
        }
    }

    onNet_player_comes_push(msg: MsgPlayerComesPush) {
        this._gameData.players.push(msg.player);
        director.getScene().emit(GomokuGameEvent.PLAYER_COMES, msg.player);
    }

    onNet_player_data_changed_push(msg: MsgPlayerDataChangedPush) {
        let p = this.getPlayer(msg.uid);
        if (!p) {
            return;
        }

        if (msg.color != undefined) {
            p.color = msg.color;
        }
    }

    onNet_player_leaves_push(msg: MsgPlayerLeavesPush) {
        for (let i = 0; i < this._gameData.players.length; ++i) {
            let p = this._gameData.players[i];
            if (p.uid == msg.uid) {
                this._gameData.players.splice(i, 1);
                director.getScene().emit(GomokuGameEvent.PLAYER_LEAVES, msg.uid);
                break;
            }
        }
    }

    onNet_game_begin_push(msg: MsgGameBeginPush) {
        if (!this._gameData) {
            return;
        }

        this._boardMgr.reset();
        this._gameData.players.forEach(v => {
            v.color = '';
        });

        director.getScene().emit(GomokuGameEvent.GAME_BEGIN);
    }

    onNet_game_over_push(msg: MsgGameOverPush) {
        director.getScene().emit(GomokuGameEvent.GAME_OVER, msg);
    }

    onNet_place_piece_push(msg: MsgPlacePiecePush) {
        this._boardMgr.setGrid(msg.gridX, msg.gridY, msg.value);
        director.getScene().emit(GomokuGameEvent.PLACE_PIECE, msg);
    }

}

SubGameMgr.inst.registerGameMgr('gomoku', GomokuGameMgr.inst);